package com.suxin.predicate;

import java.util.function.Function;
import java.util.function.Predicate;

/**
 * @author Tang
 * @classname AsyncPredicate
 * @description [  ]
 * @date 2022/9/20 15:20
 */
public interface AsyncPredicate<T> extends Function<T, Boolean> {

    static <T> AsyncPredicate<T> builder(Predicate<T> predicate) {
        return new DefaultAsyncPredicate<T>(predicate);
    }

    default AsyncPredicate<T> and(AsyncPredicate<T> predicate) {
        return new AndPredicate<T>(this, predicate);
    }

    default AsyncPredicate<T> or(AsyncPredicate<T> predicate) {
        return new OrPredicate<T>(this, predicate);
    }

    default AsyncPredicate<T> negate() {
        return new NegateAsyncPredicate<T>(this);
    }

    class DefaultAsyncPredicate<T> implements AsyncPredicate<T> {

        private final Predicate<T> predicate;

        public DefaultAsyncPredicate(Predicate<T> predicate) {
            this.predicate = predicate;
        }

        @Override
        public Boolean apply(T t) {
            return predicate.test(t);
        }
    }

    class NegateAsyncPredicate<T> implements AsyncPredicate<T> {

        private final AsyncPredicate<T> predicate;

        public NegateAsyncPredicate(AsyncPredicate<T> predicate) {
            this.predicate = predicate;
        }

        @Override
        public Boolean apply(T t) {
            return !predicate.apply(t);
        }

    }

    class AndPredicate<T> implements AsyncPredicate<T>{

        private final AsyncPredicate<T> left;
        private final AsyncPredicate<T> right;

        public AndPredicate(AsyncPredicate<T> left, AsyncPredicate<T> right) {
            this.left = left;
            this.right = right;
        }

        @Override
        public Boolean apply(T t) {
            return left.apply(t) && right.apply(t);
        }
    }

    class OrPredicate<T> implements AsyncPredicate<T> {

        private final AsyncPredicate<T> left;
        private final AsyncPredicate<T> right;

        public OrPredicate(AsyncPredicate<T> left, AsyncPredicate<T> right) {
            this.left = left;
            this.right = right;
        }

        @Override
        public Boolean apply(T t) {
            return left.apply(t) || right.apply(t);
        }
    }

}